﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using System.IO.Pipes;
using System.Diagnostics;

namespace ProgNetComponents
{
    public class SingleAppRunner
    {
        private static bool IsExiting;
        public bool IsFirstApp
        {
            get
            {
                if (!_IsStarted)
                    throw new InvalidOperationException("SingleAppRunner is not started");
                else
                    return createdNew;
            }
        }

        public string PipeName
        {
            get;
            set;
        }
        public string MutexName
        {
            get;
            set;
        }
        public string Message
        {
            get;
            set;
        }
        public Form Form
        {
            get;
            private set;
        }

        protected BackgroundWorker worker;
        protected Mutex mtx;
        protected bool createdNew;
        protected FormWindowState winStateBeforeMinimize;
        protected bool lockForm = false;
        protected bool _IsStarted = false;

        public SingleAppRunner()
        {
            worker = new BackgroundWorker();
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);

            MutexName = "Global\\"+Application.ProductName;
            PipeName = Application.ProductName;
            Message = "Można uruchomić tylko jedną instancję aplikacji.\nChcesz przełączyć się na uruchomioną aplikację?";
        }

        public void Start()
        {
            if (!_IsStarted)
            {
                _IsStarted = true;
                mtx = new Mutex(true, MutexName, out createdNew);
                if (createdNew)
                    mtx.ReleaseMutex();
            }
            else
            {
                throw new InvalidOperationException("SingleAppRunner is already started");
            }
        }

        public virtual void Run(Form mainForm)
        {
            if (!_IsStarted)
                Start();
            Form = mainForm;

            if (createdNew)
            {
                worker.RunWorkerAsync();

                Form.FormClosed += new FormClosedEventHandler(f_FormClosed);
                Form.SizeChanged += new EventHandler(f_SizeChanged);
                Form.Load += new EventHandler(Form_Load);

                Application.Run(Form);
            }
            else
            {
                try
                {
                    DialogResult dlg = ShowQuestionDlg();
                    byte[] bytes = new byte[1];

                    if (dlg == DialogResult.Yes)
                    {
                        bytes[0] = byte.MaxValue;
                    }
                    else
                    {
                        bytes[0] = byte.MinValue;
                    }
                    using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
                    {
                        pipeClient.Connect(100);
                        pipeClient.Write(bytes, 0, 1);
                        pipeClient.Flush();
                    }
                }
                catch { }
            }
            Application.Exit();
            try
            {
                System.Diagnostics.Process.GetCurrentProcess().Kill();
            }
            catch { }
        }

        void Form_Load(object sender, EventArgs e)
        {
            using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
            {
                pipeClient.Connect(100);
                pipeClient.Write(new byte[] { 0 }, 0, 1);
                pipeClient.Flush();
            }
        }

        protected virtual void ShowForm()
        {
            if (Form != null)
            {
                if (Form.InvokeRequired)
                {
                    Form.Invoke(new Action(ShowForm));
                }
                else
                {
                    try
                    {
                        lockForm = true;
                        System.Drawing.Size size = Form.Size;
                        //if (form.WindowState == FormWindowState.Minimized)
                        Form.WindowState = winStateBeforeMinimize;
                        Form.Show();
                        User32.ShowWindow(Form.Handle, User32.SW_RESTORE);
                        User32.SetForegroundWindow(Form.Handle);
                        Form.WindowState = winStateBeforeMinimize;
                        lockForm = false;
                    }
                    catch { }
                }
            }
        }

        protected virtual DialogResult ShowQuestionDlg()
        {
            return MessageBox.Show(Message, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            while (!IsExiting)
            {
                using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.In))
                {
                    pipeServer.WaitForConnection();
                    byte[] bytes = new byte[1];
                    pipeServer.Read(bytes, 0, 1);
                    if (bytes[0] == byte.MaxValue)
                    {
                        ShowForm();
                    }
                    pipeServer.Close();
                }
            }
        }

        void f_SizeChanged(object sender, EventArgs e)
        {
            if (!lockForm && (sender as Form).WindowState != FormWindowState.Minimized)
                winStateBeforeMinimize = (sender as Form).WindowState;
        }

        void f_FormClosed(object sender, FormClosedEventArgs e)
        {
            worker.CancelAsync();
            IsExiting = true;
        }
    }
}
